package com.modules.config.interceptor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import com.common.util.UserUtils;
import com.modules.upms.annotation.DataScopeAop;
import com.modules.upms.common.util.DataScopeUtils;
import com.modules.upms.entity.SysRole;
import com.modules.upms.entity.vo.AuthUserVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.sql.Connection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
 * 数据权限插件，guns
 */
@Slf4j
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataScopeInterceptor extends AbstractSqlParserHandler implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) PluginUtils.realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        this.sqlParser(metaObject);
        // 先判断是不是SELECT操作
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            return invocation.proceed();
        }

        BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
        String originalSql = boundSql.getSql();

        //查找参数中包含DataScope类型的参数
        DataScopeAop dataScope = DataScopeUtils.getDataScopeByDelegate(mappedStatement);

        if (dataScope == null) {
            return invocation.proceed();
        } else {
            String scopeName = dataScope.value();
            if(StrUtil.isNotBlank(scopeName)){
                metaObject.setValue("delegate.boundSql.sql", this.dataScopeFilter(originalSql, scopeName));
            }
            return invocation.proceed();
        }
    }

    /**
     * 生成拦截对象的代理
     *
     * @param target 目标对象
     * @return 代理对象
     */
    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    /**
     * mybatis配置的属性
     *
     * @param properties mybatis配置的属性
     */
    @Override
    public void setProperties(Properties properties) {

    }

    /**
     * 数据范围过滤
     * @return 过滤sql
     */
    private String dataScopeFilter(String originalSql, String scopeName) {

        AuthUserVO authUserVO = UserUtils.getCurrentUser();

        StringBuilder sqlString = new StringBuilder();

        // 超级管理员，跳过权限过滤
        if (authUserVO.isAdmin()) {
            return originalSql;
        }

        if(CollUtil.isEmpty(authUserVO.getRoleList())) {
            return originalSql;
        }

        boolean flag = false;
        for (SysRole sysRole : authUserVO.getRoleList()) {
            // 仅本人权限
            if ("SCOPE_SELF".equals(sysRole.getDataScope())) {
                sqlString.append("select * from (" + originalSql + ") temp_data_scope where ");
                sqlString.append("temp_data_scope.create_user_id = " + authUserVO.getUserId());
                flag = true;
                break;
            }
        }

        if(flag) {
            return sqlString.toString();
        }


        if(CollUtil.isEmpty(authUserVO.getDeptIdList())) {
            return originalSql;
        }

        sqlString.append("select * from (" + originalSql + ") temp_data_scope where ");
        sqlString.append("temp_data_scope."+ scopeName +" in(" + CollUtil.join(authUserVO.getDeptIdList(), ",") + ")");
        return sqlString.toString();
    }

}
